﻿using System.Collections;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;

namespace Mg.ExpressionParser.Parser
{
    class MemberExpressionParser : BaseExpressionParser<MemberExpression>
    {
        private SqlPack AggregateFunctionParser(MemberExpression expression, SqlPack sqlPack)
        {
            string aggregateFunctionName = new StackTrace(true).GetFrame(1).GetMethod().Name.ToLower();

            string tableName = expression.Member.DeclaringType.Name;
            string columnName = expression.Member.Name;

            sqlPack.SetTableAlias(tableName);
            string tableAlias = sqlPack.GetTableAlias(tableName);

            if (!string.IsNullOrWhiteSpace(tableAlias))
            {
                tableName += " " + tableAlias;
                columnName = tableAlias + "." + columnName;
            }
            sqlPack.Sql.AppendFormat("SELECT {0}({1}) FROM {2}", aggregateFunctionName, columnName, tableName);
            return sqlPack;
        }

        protected override SqlPack Select(MemberExpression expression, SqlPack sqlPack)
        {
            sqlPack.SetTableAlias(expression.Member.DeclaringType.Name);
            string tableAlias = sqlPack.GetTableAlias(expression.Member.DeclaringType.Name);
            if (!string.IsNullOrWhiteSpace(tableAlias))
            {
                tableAlias += ".";
            }
            sqlPack.SelectFields.Add(tableAlias + expression.Member.Name);
            return sqlPack;
        }

        protected override SqlPack Join(MemberExpression expression, SqlPack sqlPack)
        {
            sqlPack.SetTableAlias(expression.Member.DeclaringType.Name);
            string tableAlias = sqlPack.GetTableAlias(expression.Member.DeclaringType.Name);
            if (!string.IsNullOrWhiteSpace(tableAlias))
            {
                tableAlias += ".";
            }
            sqlPack += " " + tableAlias + expression.Member.Name;

            return sqlPack;
        }

        protected override SqlPack Where(MemberExpression expression, SqlPack sqlPack)
        {
            if(expression.Expression is ParameterExpression)
            {
                sqlPack.SetTableAlias(expression.Member.DeclaringType.Name);
                string tableAlias = sqlPack.GetTableAlias(expression.Member.DeclaringType.Name);
                if (!string.IsNullOrWhiteSpace(tableAlias))
                {
                    tableAlias += ".";
                }
                sqlPack += " " + tableAlias + expression.Member.Name;
            }
            else
            {
                object val = GetValue(expression);
                sqlPack.Sql.Append(' ');
                IEnumerator array = val as IEnumerator;
                if(array != null)
                {
                    AppendArray(sqlPack, array);
                }
                else if(val is IEnumerable)
                {
                    AppendArray(sqlPack, ((IEnumerable)val).GetEnumerator());
                }
                else
                    sqlPack.AddDbParameter(val);
            }
            

            return sqlPack;
        }

        /// <summary> 
        /// 获取成员表达式中的实际值
        /// </summary>
        private static object GetValue(MemberExpression expression)
        {
            object val = null;
            var field = expression.Member as FieldInfo;


            val = field != null ?
                field.GetValue(((ConstantExpression)expression.Expression).Value) :
                ((PropertyInfo)expression.Member).GetValue(((ConstantExpression)expression.Expression).Value, null);
            return val;
        }

        /// <summary>
        /// 追加可遍历对象(数组或集合或简单迭代器)
        /// </summary>
        /// <param name="sqlPack"></param>
        /// <param name="array"></param>
        private static void AppendArray(SqlPack sqlPack, IEnumerator array)
        {
            if (array.MoveNext())
            {
                sqlPack.Sql.Append('(');
                sqlPack.AddDbParameter(array.Current);
                while (array.MoveNext())
                {
                    sqlPack.Sql.Append(',');
                    sqlPack.AddDbParameter(array.Current);
                }
                sqlPack.Sql.Append(')');
            }
            else
                sqlPack.Sql.Append("NULL");
        }

        protected override SqlPack In(MemberExpression expression, SqlPack sqlPack)
        {
            var field = expression.Member as FieldInfo;
            if (field != null)
            {
                object val = field.GetValue(((ConstantExpression)expression.Expression).Value);

                if (val != null)
                {
                    string itemJoinStr = "";
                    IEnumerable array = val as IEnumerable;
                    foreach (var item in array)
                    {
                        if (field.FieldType.Name == "String[]")
                        {
                            itemJoinStr += string.Format(",'{0}'", item);
                        }
                        else
                        {
                            itemJoinStr += string.Format(",{0}", item);
                        }
                    }

                    if (itemJoinStr.Length > 0)
                    {
                        itemJoinStr = itemJoinStr.Remove(0, 1);
                        itemJoinStr = string.Format("({0})", itemJoinStr);
                        sqlPack += itemJoinStr;
                    }
                }
            }

            return sqlPack;
        }

        protected override SqlPack GroupBy(MemberExpression expression, SqlPack sqlPack)
        {
            sqlPack.SetTableAlias(expression.Member.DeclaringType.Name);
            sqlPack += sqlPack.GetTableAlias(expression.Member.DeclaringType.Name) + "." + expression.Member.Name;
            return sqlPack;
        }

        protected override SqlPack OrderBy(MemberExpression expression, SqlPack sqlPack)
        {
            sqlPack.SetTableAlias(expression.Member.DeclaringType.Name);
            sqlPack += sqlPack.GetTableAlias(expression.Member.DeclaringType.Name) + "." + expression.Member.Name;
            return sqlPack;
        }

        protected override SqlPack Max(MemberExpression expression, SqlPack sqlPack)
        {
            return AggregateFunctionParser(expression, sqlPack);
        }

        protected override SqlPack Min(MemberExpression expression, SqlPack sqlPack)
        {
            return AggregateFunctionParser(expression, sqlPack);
        }

        protected override SqlPack Avg(MemberExpression expression, SqlPack sqlPack)
        {
            return AggregateFunctionParser(expression, sqlPack);
        }

        protected override SqlPack Count(MemberExpression expression, SqlPack sqlPack)
        {
            return AggregateFunctionParser(expression, sqlPack);
        }

        protected override SqlPack Sum(MemberExpression expression, SqlPack sqlPack)
        {
            return AggregateFunctionParser(expression, sqlPack);
        }
    }
}
